﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;

namespace FitAutomation.Server
{
	[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, ConcurrencyMode = ConcurrencyMode.Reentrant)]
	class FitAutomationHostService : IFitAutomationHostService
	{
		public FitAutomationHostService()
		{
			Trace.TraceInformation("FitAutomationHostService Created: " + Process.GetCurrentProcess().Id);

		}

		readonly Dictionary<ProcessIdentifier, byte> _dic = new Dictionary<ProcessIdentifier, byte>();
		readonly Dictionary<int, ProcessIdentifier> _dicByPId = new Dictionary<int, ProcessIdentifier>();
		readonly object _sync = new object();

		DateTime _lastAccess = DateTime.UtcNow;

		internal void Live()
		{
			_lastAccess = DateTime.UtcNow;
		}

		readonly List<IFitAutomationHostServiceCallback> _callbacks = new List<IFitAutomationHostServiceCallback>();

		public void SubscribeOnEvents()
		{
			var callback = OperationContext.Current.GetCallbackChannel<IFitAutomationHostServiceCallback>();
			if(!_callbacks.Contains(callback))
			{
				_callbacks.Add(callback);
			}
		}

		public int GetHostProcessId()
		{
			Trace.TraceInformation("GetHostProcessId");
			_lastAccess = DateTime.UtcNow;
			return Process.GetCurrentProcess().Id;
		}

		public ProcessIdentifier[] GetRegisteredProcesses()
		{
			Trace.TraceInformation("GetRegisteredProcesses");
			_lastAccess = DateTime.UtcNow;
			return GetRegisteredProcessesCore();
		}

		ProcessIdentifier[] GetRegisteredProcessesCore()
		{
			lock (_sync)
			{
				return _dic.Keys.ToArray();
			}
		}

		public void RegisterProcess(ProcessIdentifier processIdentifier)
		{
			Trace.TraceInformation("RegisterProcess");
			_lastAccess = DateTime.UtcNow;
			lock (_sync)
			{
				_dic[processIdentifier] = 0;
				_dicByPId[processIdentifier.ProcessId] = processIdentifier;
			}
			OnProcessesChanged();
		}

		void OnProcessesChanged()
		{
			_callbacks.ToArray().ToList().ForEach(x =>
				{
					try
					{
						x.ProcessesAreUpdated();
					}
					catch (Exception ex)
					{
						Trace.TraceError("CallBack: " + ex.ToString());
						_callbacks.Remove(x);
						Trace.TraceInformation("CallBack Channel Removed");
					}
				});
		}

		public bool CheckAlive()
		{
			var processes = GetRegisteredProcessesCore();

			if (!processes.Any() && (DateTime.UtcNow - _lastAccess).TotalMilliseconds >= Consts.TimeoutForIncativityShutdown)
			{
				return false;
			}

			foreach (var processIdentifier in processes)
			{
				Process process;
				try
				{
					process = Process.GetProcessById(processIdentifier.ProcessId);
				}
				catch (ArgumentException)
				{
					process = null;
				}
				if (process==null || process.HasExited)
				{
					Trace.TraceInformation("Remove Process: " + processIdentifier);
					lock(_sync)
					{
						_dic.Remove(processIdentifier);
						_dicByPId.Remove(processIdentifier.ProcessId);
					}
					OnProcessesChanged();
				}
			}

			return true;
		}
	}
}